<script lang="ts">
import { Component, Vue, Model, Prop, Watch } from 'vue-property-decorator'
import { CreateElement } from 'vue'
import { globalErrorHandler } from '@/utils'
import ImageViewer from './ImageViewer.vue'

@Component({
  name: 'ImageUpload',
  components: { ImageViewer }
})
export default class extends Vue {
  @Model('change', { type: [String, Array] })
  private uploadResult!: string | Array<string>

  // size limit when uploading, byte
  @Prop({ type: Number, required: false, default: 0 })
  private sizeLimit!: number
  // placeholder
  @Prop({ type: String, required: false, default: 'upload' })
  private placeholder!: string

  private localFileList: Array<any> = []
  private loading = false
  private showPreview = false
  private previewURL = ''

  @Watch('uploadResult', { deep: true, immediate: true })
  onValueChange (
    val: string | Array<any> | undefined,
    oldVal: string | Array<any> | undefined
  ) {
    let imgList: Array<any> = []
    if (Number(this.$attrs.limit) === 1) {
      if (val) imgList = [val]
    } else {
      imgList = (val as Array<any>) || []
    }

    let same =
      this.localFileList.length === imgList.length &&
      JSON.stringify(this.localFileList.map(_ => _.url)) ===
        JSON.stringify(imgList)

    if (!same) {
      let oldLength = this.localFileList.length
      this.localFileList.length = imgList.length
      if (oldLength < imgList.length) {
        this.localFileList.fill({}, oldLength)
      }

      this.localFileList.forEach((_, i) => {
        _.name = `${i}`
        _.url = imgList[i]
      })
    }
  }

  private onPreview (file: any): void {
    this.previewURL = file.url
    this.showPreview = true
  }
  private onRemove (file: any, fileList: Array<any>): void {
    this.output(fileList)
    this.$emit('remove', [...fileList])
  }
  private onSuccess (res: any, file: any, fileList: Array<any>): void {
    this.loading = false
    this.output(fileList)
    this.$emit('success', res, [...fileList])
  }
  private onError (err: Error) {
    this.loading = false
    this.$message.error('upload failed')
    globalErrorHandler(err)
  }
  private beforeUpload (file: File) {
    this.loading = true

    if (this.sizeLimit === 0) {
      return true
    } else {
      let size = file.size <= this.sizeLimit

      if (!size) {
        this.$message.warning('the selected image is too large')
        this.loading = false
      }

      return size
    }
  }
  private output (list: Array<any>) {
    this.localFileList = list
    let arr = list.map(_ => _.url)
    if (Number(this.$attrs.limit) === 1) {
      this.$emit('change', arr.join())
    } else {
      this.$emit('change', arr)
    }
  }

  render (h: CreateElement) {
    let upload = h(
      'el-upload',
      {
        class: {
          'hide-el-upload-trigger':
            Number(this.$attrs.limit) === this.localFileList.length
        },
        props: {
          accept: 'image/*',
          'on-preview': this.onPreview,
          'on-remove': this.onRemove,
          'on-success': this.onSuccess,
          'on-error': this.onError,
          'before-upload': this.beforeUpload,
          'list-type': 'picture-card',
          ...this.$attrs,
          'file-list': this.localFileList
        }
      },
      [
        h(
          'div',
          {
            class: 'image-upload-placeholder',
            attrs: { title: this.placeholder }
          },
          [
            h('i', { class: 'el-icon-picture image-upload-placeholder-icon' }),
            h('p', { class: 'image-upload-placeholder-text' }, this.placeholder)
          ]
        )
      ]
    )
    let preview = h('ImageViewer', {
      props: { visible: this.showPreview, src: this.previewURL },
      on: {
        'update:visible': (v: boolean) => {
          this.showPreview = v
        }
      }
    })

    return h('div', [upload, preview])
  }
}
</script>

<style lang="scss">
.hide-el-upload-trigger {
  > div {
    display: none;
  }
}
</style>

<style lang="scss" scoped>
.image-upload-placeholder {
  width: 100%;
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  > .image-upload-placeholder-icon {
    transform: translateY(-10px);
  }
  > .image-upload-placeholder-text {
    box-sizing: border-box;
    width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    line-height: 12px;
    font-size: 12px;
    color: $info;
    margin: 0;
    padding: 0;
  }
}
</style>
